#include "chatservice.hpp"
#include "public.hpp"
#include "CommonConnectionPool.hpp"
#include "usermodel.hpp"
#include "offlinemessagemodel.hpp"
#include "friendmodel.hpp"
#include "groupmodel.hpp"

#include <mymuduo/Logger.h>
#include <string>

using namespace std;

// 单例指针定义
ChatService *ChatService::_service = nullptr;
// 单例接口
ChatService *ChatService::instance()
{
    if (_service == nullptr)
    {
        _service = new ChatService;
    }
    return _service;
}

// 注册消息以及对应的回调函数
ChatService::ChatService()
{
    _msgHandlerMap.insert({LOGIN_MSG, std::bind(&ChatService::login, this, _1, _2, _3)});
    _msgHandlerMap.insert({REG_MSG, std::bind(&ChatService::reg, this, _1, _2, _3)});
    _msgHandlerMap.insert({SINGLE_CHAT_MSG, std::bind(&ChatService::singleChat, this, _1, _2, _3)});
    _msgHandlerMap.insert({ADD_FRIEND_MSG, std::bind(&ChatService::addFriend, this, _1, _2, _3)});
    _msgHandlerMap.insert({CREATE_GROUP_MSG, std::bind(&ChatService::createGroup, this, _1, _2, _3)});
    _msgHandlerMap.insert({ADD_GROUP_MSG, std::bind(&ChatService::addGroup, this, _1, _2, _3)});
    _msgHandlerMap.insert({GROUP_CHAT_MSG, std::bind(&ChatService::groupChat, this, _1, _2, _3)});

    //连接redis服务器
    if(_redis.connect()){
        _redis.init_notify_handler(std::bind(&ChatService::redisReadHandler, this, _1, _2));
    }
}

// 登录Handler
void ChatService::login(const TcpConnectionPtr &conn, json &js, Timestamp&)
{
    LOG_INFO("进行登录操作");
    // 反序列化获得用户名和密码
    string name = js["name"];
    string pwd = js["password"];

    User user(name, pwd);
    bool state = UserModel::queryByName(user);

    if (state && // 判断查询成功
        user.getId() != -1 &&
        pwd == user.getPassword() && // 验证密码
        user.getState() == "offline" // 判断离线
    )
    {// 登录成功

        // 更新数据库状态
        user.setState("online");
        if (!UserModel::update(user))
        {
            // 更新数据库失败
            json response;
            response["msgid"] = LOGIN_MSG_ACK;
            response["errno"] = 1;
            response["errmsg"] = "登录失败,User数据库更新错误";
            conn->send(response.dump());
            return;
        }

        // 互斥地插入在线用户的连接
        {
            lock_guard<mutex> guard(_Mutex);
            _onlineUserConnectionMap.insert({user.getId(), conn}); 
        }

        //向redis订阅id
        _redis.subscribe(user.getId());

        //登录成功的json回复
        json response;
        response["msgid"] = LOGIN_MSG_ACK;
        response["errno"] = 0;
        response["id"] = user.getId();
    
        //查询该用户好友列表并返回
        vector<vector<string>> vec2 = FriendModel::query(user.getId());
        if(!vec2.empty()) response["friends"] = vec2;
        //查询该用户群组信息并返回
        vector<Group> groupuserVec = GroupModel::queryGroups(user.getId());
        if(!groupuserVec.empty()){
            vector<string> groupV;
            for(Group &group : groupuserVec){
                json grpjson;
                grpjson["id"] = group.getId();
                grpjson["groupname"] = group.getName();
                grpjson["groupdesc"] = group.getDesc();
                vector<string> userV;
                for(GroupUser &user:group.getUsers()){
                    json js;
                    js["id"] = user.getId();
                    js["name"] = user.getName();
                    js["state"] = user.getState();
                    js["role"] = user.getRole();
                    userV.emplace_back(js.dump());
                }
                grpjson["users"] = userV;
                groupV.emplace_back(grpjson.dump());
            }
            response["groups"] = groupV;
        }

        //检查用户是否有离线消息
        vector<string> vec = OfflineMsgModel::queryMsg(user.getId());
        if(!vec.empty()){
            response["offlinemsg"] = vec;
            OfflineMsgModel::deleteMsg(user.getId());
        }

        //发送消息
        conn->send(response.dump());
    }
    else
    {
        // 登陆失败
        json response;
        response["msgid"] = LOGIN_MSG_ACK;
        response["errno"] = 1;
        response["id"] = user.getId();
        response["errmsg"] = "登陆失败,用户名密码错误或已经在线";
        conn->send(response.dump());
    }
}

// 注册Handler
void ChatService::reg(const TcpConnectionPtr &conn, json &js, Timestamp&)
{
    LOG_INFO("进行注册操作");
    // 反序列化注册的用户名和密码
    string name = js["name"];
    string pwd = js["password"];

    User user(name, pwd);
    bool state = UserModel::insert(user);

    if (state)
    {
        // 注册成功
        json response;
        response["msgid"] = REG_MSG_ACK;
        response["errno"] = 0;
        response["id"] = user.getId();
        conn->send(response.dump());
    }
    else
    {
        // 注册失败
        json response;
        response["msgid"] = REG_MSG_ACK;
        response["errno"] = 1;
        response["id"] = user.getId();
        response["errmsg"] = "注册失败";
        conn->send(response.dump());
    }
}

// 点对点聊天的Handler
void ChatService::singleChat(const TcpConnectionPtr&, json& js, Timestamp&){ 
    int toId = js["to_id"];

    //查询对方是否在线
    User user("","","offline",toId);
    UserModel::queryById(user);

    //加锁判断 _onlineUserConnectionMap中用户是否在线
    {
        lock_guard<std::mutex> guard(_Mutex);
        auto iter = _onlineUserConnectionMap.find(toId);
        if(iter != _onlineUserConnectionMap.end()){
            //对方用户在线,直接发送js
            iter->second->send(js.dump());
        }else if(user.getState() == "online"){
            //对方在另一个服务器上，发送redis消息
            _redis.publish(toId, js.dump());
        }else{
            //对方用户不在线
            OfflineMsgModel::insertMsg(toId,js.dump());
        }
    }       

}

// 获取消息对应的Handler
MsgHandler ChatService::getHandler(int index)
{
    auto iter = _msgHandlerMap.find(index);
    // 不存在handler
    if (iter == _msgHandlerMap.end())
    {
        LOG_ERROR("没找到消息ID为 %d 的handler", index);
        // 返回一个默认的空操作handler
        return [](const TcpConnectionPtr &, json &, Timestamp) {};
    }
    return _msgHandlerMap[index];
}

void ChatService::onErrorDisconnect(const TcpConnectionPtr &conn)
{
    User user;
    // 加锁从map中查找conn获取用户id并且从map中删除conn
    {
        lock_guard<mutex> guard(_Mutex);
        for (auto it = _onlineUserConnectionMap.begin(); it != _onlineUserConnectionMap.end(); it++)
        {
            if (it->second == conn)
            {
                // 找到conn
                user.setId(it->first);
                _onlineUserConnectionMap.erase(it);
                LOG_INFO("用户[id=%d]异常断开连接", user.getId());
                break;
            }
        }
    }
    // 取消redis订阅
    _redis.unsubscribe(user.getId());
    // 根据userId更新数据库状态
    UserModel::queryById(user);
    user.setState("offline");
    UserModel::update(user);
}

//重置所有用户状态，用于服务器异常退出
void ChatService::reset(){
    UserModel::resetUserState();
}

//添加好友的业务
void ChatService::addFriend(const TcpConnectionPtr& conn, json& js, Timestamp&){
    int userid = js["id"].get<int>();
    int friendid = js["friendid"].get<int>();

    //检查好友id是否存在
    User user("","","offline",userid);
    if(!UserModel::queryById(user)){
        //对方id不存在
        json response;
        response["msgid"] = ADD_FRIEND_ACK;//添加好友的消息ACK;
        response["errno"] = 1;
        response["errmsg"] = "对方id不存在";
        conn->send(response.dump());
        return;
    }

    //存储好友信息
    if(!FriendModel::insert(userid, friendid)){
        //存储失败
        json response;
        response["msgid"] = ADD_FRIEND_ACK;//添加好友的消息ACK;
        response["errno"] = 1;
        response["errmsg"] = "对方已是好友";
        conn->send(response.dump());
        return;
    }

    //添加成功
        json response;
        response["msgid"] = ADD_FRIEND_ACK;//添加好友的消息ACK;
        response["errno"] = 0;
        conn->send(response.dump());
        return;
}

//创建群组业务
void ChatService::createGroup(const TcpConnectionPtr& conn, json& js, Timestamp&){
    //获取json信息
    int userid = js["id"].get<int>();
    string name = js["groupname"];
    string desc = js["groupdesc"];

    //存储新创建的群组信息
    Group group(-1, name, desc);
    if(GroupModel::createGroup(group)){
        //存储群组创建人的信息
        GroupModel::addGroup(userid, group.getId(), "creator");
    }
    //todo:ack信息回复客户端
}

//加入群组业务
void ChatService::addGroup(const TcpConnectionPtr& conn, json& js, Timestamp&){
    //获取json信息
    int userid = js["id"].get<int>();
    int groupid = js["groupid"].get<int>();
    GroupModel::addGroup(userid, groupid, "normal");
    //todo:ack信息回复客户端
}

//群组聊天业务
void ChatService::groupChat(const TcpConnectionPtr& conn, json& js, Timestamp&){
    //获取json信息
    int userid = js["id"].get<int>();
    int groupid = js["groupid"].get<int>();

    //查询群组中其他用户的id信息
    vector<int> useridVec = GroupModel::queryGroupUsers(userid,groupid);
    
    //定义一个User对象用于查询数据库状态，判断是否在另一个服务器上
    User user;

    //循环群发信息
    lock_guard<mutex> lock(_Mutex);//加锁
    for(const int& id : useridVec){
        auto it = _onlineUserConnectionMap.find(id);
        if(it != _onlineUserConnectionMap.end()){
            //对方在线，直接发送
            it->second->send(js.dump());
        }else{
            user.setId(id);
            UserModel::queryById(user);
            if(user.getState() == "online"){
                //用户在另一个服务器上，发布到redis
                _redis.publish(id, js.dump());
            }else{
                //用户离线
                OfflineMsgModel::insertMsg(id, js.dump());
            }
        }
    }
}

//从redis获得消息的回调函数
void ChatService::redisReadHandler(int userid, string msg){
    //加锁判断 _onlineUserConnectionMap中用户是否在线
    {
        lock_guard<std::mutex> guard(_Mutex);
        auto iter = _onlineUserConnectionMap.find(userid);
        if(iter != _onlineUserConnectionMap.end()){
            //对方用户在线,直接发送js
            iter->second->send(msg);
        }else{
            //对方用户不在线
            OfflineMsgModel::insertMsg(userid,msg);
        }
    }       

}

